package com.fis.vaadin;

import java.lang.reflect.Method;
import java.text.DecimalFormat;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Vector;

import com.fis.vaadin.define.Properties;
import com.fis.vaadin.define.Resolution;
import com.fis.vaadin.format.PropertyFormatter;
import com.fis.vaadin.utils.StringUtil;
import com.fss.dictionary.DictionaryNode;
import com.vaadin.terminal.ThemeResource;
import com.vaadin.ui.AbstractSelect.MultiSelectMode;
import com.vaadin.ui.Button;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.DateField;
import com.vaadin.ui.Embedded;
import com.vaadin.ui.Link;
import com.vaadin.ui.ListSelect;
import com.vaadin.ui.MenuBar;
import com.vaadin.ui.OptionGroup;
import com.vaadin.ui.PasswordField;
import com.vaadin.ui.RichTextArea;
import com.vaadin.ui.Slider;
import com.vaadin.ui.Table;
import com.vaadin.ui.TextArea;
import com.vaadin.ui.TextField;
import com.vaadin.ui.Tree;
import com.vaadin.ui.TreeTable;
import com.vaadin.ui.Upload;

import de.essendi.vaadin.ui.component.numberfield.NumberField;

public class ControlCreatorImpl implements ControlCreator {

	@Override
	public TextField createTextControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		TextField control = (TextField) createVaadinControl(
				com.vaadin.ui.TextField.class, component, dictionaryNode);
		
		return control;

	}

	@Override
	public Upload createFileControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Upload control = (Upload) createVaadinControl(
				com.vaadin.ui.Upload.class, component, dictionaryNode);
		
		return control;

	}

	// @SuppressWarnings({ "unchecked", "serial" })
	@Override
	public NumberField createNumberControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		// String strFormat = dictionaryNode
		// .getStringIgnoreCase(Properties.FORMAT);

		NumberField control = (NumberField) createVaadinControl(
				de.essendi.vaadin.ui.component.numberfield.NumberField.class,
				component, dictionaryNode);

		// if (!strFormat.equals("")) {
		//
		// final Format format = new DecimalFormat(strFormat);
		//
		// ((NumberField) control)
		// .setPropertyDataSource(new com.vaadin.data.util.PropertyFormatter() {
		//
		// @Override
		// public Object parse(String formattedValue)
		// throws Exception {
		//
		// return Double.parseDouble(formattedValue);
		//
		// }
		//
		// @Override
		// public String format(Object value) {
		//
		// return format.format(value);
		//
		// }
		// });
		//
		// }

		return control;

	}

	@Override
	public TextArea createTextAreaControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		TextArea control = (TextArea) createVaadinControl(
				com.vaadin.ui.TextArea.class, component, dictionaryNode);

		return control;

	}

	@Override
	public PasswordField createPasswordControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		PasswordField control = (PasswordField) createVaadinControl(
				com.vaadin.ui.PasswordField.class, component, dictionaryNode);

		return control;

	}

	@Override
	public Tree createTreeControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Tree control = (Tree) createVaadinControl(com.vaadin.ui.Tree.class,
				component, dictionaryNode);

		return control;

	}

	@Override
	public ComboBox createComboBoxControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		ComboBox control = (ComboBox) createVaadinControl(
				com.vaadin.ui.ComboBox.class, component, dictionaryNode);

		String strNodeValue = dictionaryNode
				.getStringIgnoreCase(Properties.ALLOW_NULL_SELECT);

		if (!strNodeValue.equals("")) {

			if (strNodeValue.equalsIgnoreCase(Properties.FALSE))
				control.setNullSelectionAllowed(false);
			else
				control.setNullSelectionAllowed(true);

		}

		return control;

	}

	@Override
	public Table createTableControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Table control = (Table) createVaadinControl(com.vaadin.ui.Table.class,
				component, dictionaryNode);

		buildTableComponent(dictionaryNode, control);

		return control;

	}

	@Override
	public CheckBox createCheckBoxControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		CheckBox control = (CheckBox) createVaadinControl(
				com.vaadin.ui.CheckBox.class, component, dictionaryNode);

		return control;

	}

	@Override
	public Button createButtonControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Button control = (Button) createVaadinControl(
				com.vaadin.ui.Button.class, component, dictionaryNode);

		return control;

	}

	@Override
	public Component createUserDefinedControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		String strClassName = dictionaryNode
				.getStringIgnoreCase(Properties.CLASS);

		Component control = (Component) createVaadinControl(
				Class.forName(strClassName), component, dictionaryNode);

		return control;

	}

	@Override
	public DateField createDateControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		DateField dateField = (DateField) createVaadinControl(
				com.vaadin.ui.DateField.class, component, dictionaryNode);

		String strFormat = dictionaryNode
				.getStringIgnoreCase(Properties.FORMAT);
		String strResolution = dictionaryNode.getStringIgnoreCase(
				Properties.RESOLUTION).toLowerCase();

		if (!strFormat.equals(""))
			dateField.setDateFormat(strFormat);

		if (strResolution.equals(Resolution.RESOLUTION_DAY))
			dateField.setResolution(DateField.RESOLUTION_DAY);
		else if (strResolution.equals(Resolution.RESOLUTION_HOUR))
			dateField.setResolution(DateField.RESOLUTION_HOUR);
		else if (strResolution.equals(Resolution.RESOLUTION_MIN))
			dateField.setResolution(DateField.RESOLUTION_MIN);
		else if (strResolution.equals(Resolution.RESOLUTION_MONTH))
			dateField.setResolution(DateField.RESOLUTION_MONTH);
		else if (strResolution.equals(Resolution.RESOLUTION_MSEC))
			dateField.setResolution(DateField.RESOLUTION_MSEC);
		else if (strResolution.equals(Resolution.RESOLUTION_SEC))
			dateField.setResolution(DateField.RESOLUTION_SEC);
		else if (strResolution.equals(Resolution.RESOLUTION_YEAR))
			dateField.setResolution(DateField.RESOLUTION_YEAR);

		return dateField;
	}

	// ////////////////////////////////////////////////////////////////////
	@Override
	public Link createLinkControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Link control = (Link) createVaadinControl(com.vaadin.ui.Link.class,
				component, dictionaryNode);

		return control;

	}

	@Override
	public Embedded createEmbeddedControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Embedded control = (Embedded) createVaadinControl(
				com.vaadin.ui.Embedded.class, component, dictionaryNode);

		return control;

	}

	@Override
	public RichTextArea createRichTextControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		RichTextArea control = (RichTextArea) createVaadinControl(
				com.vaadin.ui.RichTextArea.class, component, dictionaryNode);

		return control;

	}

	@Override
	public ListSelect createListSelectControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		ListSelect control = (ListSelect) createVaadinControl(
				com.vaadin.ui.ListSelect.class, component, dictionaryNode);

		return control;

	}

	@Override
	public OptionGroup createOptionGroupControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		OptionGroup control = (OptionGroup) createVaadinControl(
				com.vaadin.ui.OptionGroup.class, component, dictionaryNode);

		return control;

	}

	@Override
	public Slider createSliderControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		Slider control = (Slider) createVaadinControl(
				com.vaadin.ui.Slider.class, component, dictionaryNode);

		return control;

	}

	@Override
	public TreeTable createTreeTableControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		TreeTable control = (TreeTable) createVaadinControl(
				com.vaadin.ui.TreeTable.class, component, dictionaryNode);

		buildTableComponent(dictionaryNode, control);

		return control;

	}

	@Override
	public MenuBar createMenuBarControl(Component component,
			DictionaryNode dictionaryNode) throws Exception {

		MenuBar control = (MenuBar) createVaadinControl(
				com.vaadin.ui.MenuBar.class, component, dictionaryNode);
		
		boolean bIsAutoOpen = StringUtil.parseBoolean(dictionaryNode
				.getStringIgnoreCase(Properties.AUTO_OPEN));
		boolean bHtmlAllowed = StringUtil.parseBoolean(dictionaryNode
				.getStringIgnoreCase(Properties.HTML_CONTENT_ALLOWED));

		control.setAutoOpen(bIsAutoOpen);
		control.setHtmlContentAllowed(bHtmlAllowed);

		return control;

	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Object createVaadinControl(Class controlClass, Component component,
			DictionaryNode dictionaryNode) throws Exception {

		DictionaryNode dic;

		try {

			Object obj;
			dic = dictionaryNode.getChildIgnoreCase(Properties.METHOD);

			if (dic != null) {
				String strMethod = dic.getValue();

				if (!strMethod.equals("")) {
					try {

						Method method = component.getClass().getMethod(
								strMethod, new Class[0]);
						obj = method.invoke(component, new Object[0]);

						if (obj == null)
							throw new InvokeException(
									String.format(
											"Can't create control %s. Method %s return null value\n\tAt: %s",
											dictionaryNode.mstrName, strMethod,
											dic.getTrace()));

						if (!controlClass.isAssignableFrom(obj.getClass()))
							throw new InvokeException(
									String.format(
											"Can't create control %s. Method %s does not return a %s\n\tAt: %s",
											dictionaryNode.mstrName, strMethod,
											controlClass.getName(),
											dic.getTrace()));

						return obj;

					} catch (NoSuchMethodError nsme) {
						throw new NoSuchMethodException(
								String.format(
										"Can'n create control %s. Method %s is not defined in this form\n\tAt: %s",
										dictionaryNode.mstrName, strMethod,
										dic.getTrace()));
					}
				}
			} else if ((dic = dictionaryNode
					.getChildIgnoreCase(Properties.CLASS)) != null) {
				String strClass = dic.getValue();

				if (!strClass.equals("")) {
					obj = Class.forName(strClass).newInstance();

					if (!controlClass.isAssignableFrom(obj.getClass()))
						throw new InvokeException(
								String.format(
										"Can't create control %s. Class %s is not a %s\n\tAt: %s",
										dictionaryNode.mstrName, strClass,
										controlClass.getName(), dic.getTrace()));

					return obj;
				}
			}

			return controlClass.newInstance();

		} catch (Exception e) {
			throw e;
		}
	}

	private void buildTableComponent(DictionaryNode dictionaryNode,
			Table control) throws Exception {

		String strSubValue = dictionaryNode
				.getStringIgnoreCase(Properties.SELECTION_MODE);

		if (strSubValue != null) {

			if (strSubValue
					.equalsIgnoreCase(Properties.SELECTION_MULTI_INTERVAL)) {
				control.setMultiSelect(true);
				control.setMultiSelectMode(MultiSelectMode.DEFAULT);
			} else if (strSubValue
					.equalsIgnoreCase(Properties.SELECTION_SINGLE_INTERVAL)) {
				control.setMultiSelect(true);
				control.setMultiSelectMode(MultiSelectMode.SIMPLE);
			} else if (strSubValue
					.equalsIgnoreCase(Properties.SELECTION_SINGLE)) {
				control.setMultiSelect(false);
			}
		}

		strSubValue = dictionaryNode
				.getStringIgnoreCase(Properties.ALLOW_SELECT);

		if (strSubValue != null) {

			if (strSubValue.equalsIgnoreCase(Properties.TRUE))
				control.setSelectable(true);
			else if (strSubValue.equalsIgnoreCase(Properties.FALSE))
				control.setSelectable(false);

		}

		addTableColumns(control, dictionaryNode);

	}

	@SuppressWarnings("rawtypes")
	private Table addTableColumns(Table table, DictionaryNode dictionaryNode)
			throws Exception {

		DictionaryNode dnColumn;

		DictionaryNode dnColumns = dictionaryNode
				.getChildIgnoreCase(Properties.COLUMN);

		String strColumnName;
		String strAlignment;
		String strIcon;

		if (dnColumns != null) {

			Vector vtColumns = dnColumns.getChildList();

			for (int i = 0; i < vtColumns.size(); ++i) {

				dnColumn = (DictionaryNode) vtColumns.elementAt(i);
				strColumnName = dnColumn.getValue();
				strAlignment = dnColumn.getStringIgnoreCase(Properties.ALIGN);
				strIcon = dnColumn.getStringIgnoreCase(Properties.ICON);

				Format format = null;
				DictionaryNode dnFormatType = dnColumn
						.getChildIgnoreCase(Properties.FORMAT_TYPE);
				int colWidth = StringUtil.parseInt(dnColumn
						.getStringIgnoreCase(Properties.WIDTH));

				if (dnFormatType != null) {

					String strFormatType = dnFormatType.getValue();
					String strFormat;

					if (!strFormatType.equals("")) {

						if (strFormatType.equalsIgnoreCase(Properties.DATE)) {

							strFormat = dnColumn
									.getStringIgnoreCase(Properties.FORMAT);

							format = new SimpleDateFormat(strFormat);

						} else if (strFormatType
								.equalsIgnoreCase(Properties.NUMBER)) {

							strFormat = dnColumn
									.getStringIgnoreCase(Properties.FORMAT);

							format = new DecimalFormat(strFormat);

						} else
							throw new UnsupportedException(
									String.format(
											"Format type %s has not been supported\n\tAt: %s",
											strFormatType,
											dnFormatType.getTrace()));

					}

				}

				table.addContainerProperty(strColumnName, Object.class, null,
						dnColumn.mstrName, strIcon.equals("") ? null
								: new ThemeResource(strIcon), Table.ALIGN_LEFT);

				table.addGeneratedColumn(strColumnName, new PropertyFormatter(
						format));

				if (colWidth > 0)
					table.setColumnWidth(strColumnName, colWidth);

				if (strAlignment != null) {

					if (strAlignment.equals(Properties.ALIGN_CENTER))
						table.setColumnAlignment(strColumnName,
								Table.ALIGN_CENTER);
					else if (strAlignment.equals(Properties.ALIGN_LEFT))
						table.setColumnAlignment(strColumnName,
								Table.ALIGN_LEFT);
					else if (strAlignment.equals(Properties.ALIGN_RIGHT))
						table.setColumnAlignment(strColumnName,
								Table.ALIGN_RIGHT);

				}

			}

		}

		return table;

	}
}
